package com.hfut.eeg.common.component;

import cn.hutool.core.lang.Console;
import com.hfut.eeg.common.eegParse.EEGDataParse;
import com.hfut.eeg.common.eegParse.EEGcontrl;
import com.hfut.eeg.common.eegParse.Hdr;
import com.hfut.eeg.common.util.BlobListTrans;
import com.hfut.eeg.common.util.EEGUtil;
import com.hfut.eeg.dto.CheckState;
import com.hfut.eeg.service.PatientInfoService;
import com.hfut.eeg.socket.AiServer;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;

/**
 * @author 梁琦
 * @program: eeg
 * @Package com.hfut.eeg.common.component
 * @Description: EEG文件监听器
 * @date 2019-11-23 14:25
 */
@Component
@Scope("singleton")
public class EEGfileListener extends FileAlterationListenerAdaptor {

    private static final Logger LOGGER = LoggerFactory.getLogger(EEGfileListener.class);

    @Autowired
    private EEGcache eeGcache;

    @Autowired
    private EEGcontrl eeGcontrl;

    @Autowired
    private PatientInfoService service;

    //设置检查状态
    private boolean checkState = false;

    @Autowired
    private RealTimeEEG realTimeEEG;

    @Autowired
    private AiServer aiServer;

    //EEG数据开始位置，用于获取额外数据
    private Long sampleBegin =0L;

    private Long sampleEnd = 0L;

    //存储EEG文件的路径
    private String filePath = "";

    //设置HDR
    private Hdr hdr;

    private Long time = 0L;

    private Long eegDataNum = 0L;

    //设置文件状态是否改变，默认不改变
    //当检查完毕时，应该设置状态为false！！
    private boolean fileStatus=false;

    public Long getEegDataNum() {
        return eegDataNum;
    }

    public void setEegDataNum(Long eegDataNum) {
        this.eegDataNum = eegDataNum;
    }

    public boolean isCheckState() {
        return checkState;
    }

    public void setCheckState(boolean checkState) {
        this.checkState = checkState;
    }

    @Override
    public void onDirectoryChange(File directory) {
        LOGGER.info("目录: "+directory.getName()+"被创建");
    }

    @Override
    public void onDirectoryDelete(File directory) {
        LOGGER.info("目录: "+directory.getName()+"被删除");
    }

    public void reset(){
        setFileStatus(false);
        setSampleBegin(0L);
        setSampleEnd(0L);
        setEegDataNum(0L);
    }

    /**
    * @Description: 监听文件是否被创建
    * @param file 被创建的文件
    * @return
    * @author 梁琦
    * @date 2019-12-30 14:03
    * @throws
    */
    @Override
    public void onFileCreate(File file) {
        fileStatus = true;
        LOGGER.info("文件: "+file.getName()+"被创建");
        //首先设置EEG文件所在的位置信息，必须的！
        try {
            filePath = file.getCanonicalPath();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //防止beginSample和endSample信息不正确
        setSampleBegin(0L);
        setSampleEnd(0L);

        ArrayList<ArrayList> lists;
        Hdr hdr;
        //测试是否被其他程序占用
        if(file.canRead() && file.canWrite()){
            EEGDataParse parse = new EEGDataParse();
            try (RandomAccessFile aFile = new RandomAccessFile(filePath,"r")){
                hdr= parse.needhdr(filePath);
                setHdr(hdr);
                //在文件开始创建的时候，从0到文件末尾获取数据
                Long nSamples = hdr.getnSamples();
                Long readEEGLen = nSamples>5000?5000:nSamples;//计算需要读取数据的长度
                lists = eeGcontrl.readEEG(parse,hdr,nSamples-readEEGLen,nSamples);
                if(lists.get(0).size()>0 && nSamples>0 && !nSamples.equals(eegDataNum)){
                    //设置下一次读取数据的位置
                    eegDataNum = nSamples;
                    setSampleBegin(nSamples);
                    LOGGER.info("创建：文件总量："+nSamples);
                    //可以开始进行数据获取
                    //此时前台应该已经点击开始按钮
                    if(checkState){
                        time = System.currentTimeMillis();
                        //向cache缓存中添加数据
                        ArrayList<ArrayList> tmp = new ArrayList<>(lists);
                        realTimeEEG.setEEG(EEGUtil.deepCopy(tmp));
                        for (int i=0;i<lists.get(0).size()/500;i++){
                            ArrayList<ArrayList> mp = splice(lists,500*i,500*i+500);
                            if(mp!=null && mp.size()==18 && mp.get(0).size()==500){
                                aiServer.setCachePools(EEGUtil.deepCopy(mp));
                            }
                        }
                        eeGcache.addDataToPool(EEGUtil.deepCopy(lists));
                    }
                }
            } catch (Exception e) {
                LOGGER.warn("文件："+filePath+"正在被占用");
                try {
                    Thread.sleep(100);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                //文件被占用，机会调用函数
                onFileCreate(file);
            }
        }else {
            //若被占用，就让线程沉睡100毫秒。然后再执行
            try {
                Thread.sleep(100);
                onFileCreate(file);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //更新病人检查时间
        if(service.updatePatientCheckTime(eeGcache.getPatientId())<1){
            LOGGER.error("Listener 更新病人检查时间失败");
        }
    }

    //监听文件发生改变
    //此时应该获取多额外的EEG数据
    @Override
    public void onFileChange(File file) {
        fileStatus = true;
        LOGGER.info("文件: "+file.getName()+"被更新");
        //证明文件路径尚未被初始化，文件尚未被发现
        if(filePath.equals("")){
            onFileCreate(file);
            return;
        }

        ArrayList<ArrayList> lists;
        //每次更新都需要重新定义Hdr，因为hdr.getnSamples()是有其中字节具体信息计算得到
        EEGDataParse parse = new EEGDataParse();
        Hdr hdr = parse.needhdr(filePath);
        Long nSamples = hdr.getnSamples();
        Long readEEGLen = nSamples-sampleBegin;//计算需要读取数据的长度
        lists = eeGcontrl.readEEG(parse,hdr,nSamples-readEEGLen,nSamples);

        //想cache缓存中添加数据
        if(lists.get(0).size()>0 && nSamples>0 && !nSamples.equals(eegDataNum)){
            LOGGER.info("更新：文件总量："+nSamples);
            Console.log("onFileChange: sampleBegin:"+sampleBegin+" size: "+lists.get(0).size());
            eegDataNum = nSamples;
            //设置下一次读取数据的位置
            setSampleBegin(nSamples);
            //开始数据收集
            if(checkState){
                //准备向前台传输实时数据
                LOGGER.info("时间差："+(System.currentTimeMillis()-time));
                time = System.currentTimeMillis();
                ArrayList<ArrayList> tmp = new ArrayList<>(lists);
                realTimeEEG.setEEG(EEGUtil.deepCopy(tmp));
                for (int i=0;i<lists.get(0).size()/500;i++){
                    ArrayList<ArrayList> mp = splice(lists,500*i,500*i+500);
                    if(mp!=null && mp.size()==18 && mp.get(0).size()==500){
                        aiServer.setCachePools(EEGUtil.deepCopy(mp));
                    }
                }
                eeGcache.addDataToPool(EEGUtil.deepCopy(lists));
            }
        }

    }

    /**
    * 进行数组的切割操作，主要是获取到数据可能是ns的数据，需要一秒一秒进行传输
    */
    public ArrayList<ArrayList> splice(ArrayList<ArrayList> lists,int start,int end){
        ArrayList<ArrayList> result = new ArrayList<>();
        for (ArrayList list:lists){
            result.add(new ArrayList(list.subList(start,end)));
        }
        return result;
    }

    @PostConstruct
    public void init(){
        eeGcache.getEegThread().setEeGcache(eeGcache);
    }

    @Override
    public void onFileDelete(File file) {
        super.onFileDelete(file);
    }

    public EEGcache getEeGcache() {
        return eeGcache;
    }

    public void setEeGcache(EEGcache eeGcache) {
        this.eeGcache = eeGcache;
    }

    public Long getSampleBegin() {
        return sampleBegin;
    }

    public void setSampleBegin(Long sampleBegin) {
        this.sampleBegin = sampleBegin;
    }

    public Long getSampleEnd() {
        return sampleEnd;
    }

    public void setSampleEnd(Long sampleEnd) {
        this.sampleEnd = sampleEnd;
    }

    public String getFilePath() {
        return filePath;
    }

    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }

    public Hdr getHdr() {
        return hdr;
    }

    public void setHdr(Hdr hdr) {
        this.hdr = hdr;
    }

    public boolean isFileStatus() {
        return fileStatus;
    }

    public void setFileStatus(boolean fileStatus) {
        this.fileStatus = fileStatus;
    }
}
